package ru.simplemaps.osmapi;

import org.xml.sax.SAXException;
import ru.simplemaps.osmapi.cache.CacheInformation;
import ru.simplemaps.osmapi.cache.OSMCache;
import ru.simplemaps.osmapi.common.BBox;
import ru.simplemaps.osmapi.objects.Relation;
import ru.simplemaps.osmapi.objects.Way;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.*;

/**
 * Full service for retrieving mapping information.
 * Uses cache, updates data if needed and possible
 *
 * @author enaku_adm
 * @since 22.07.2010 17:07:28
 */
public class FullOSMService {
    /**
     * размер сетки, которая выбирается за один раз
     */
    public static final double NET_SIZE = 0.1;

    private OSMService service;
    private OSMCache cache;

    public FullOSMService(OSMService service, OSMCache cache) {
        this.service = service;
        this.cache = cache;
    }

    /**
     * Retrieve map data for specified box
     *
     * @param box bounding box
     * @return retreived data
     */
    public OSMData getDataByBbox(BBox box) throws IOException, SAXException, ParserConfigurationException {
        //Вычисление границ, которые нужно
        CacheInformation i = cache.getIntersectionWithCache(box);
        Collection<BBox> boxesToLoad = box.minus(i.found);
        //Вычисление областей, которые нужно загрузить
        Collection<BBox> simpleBoxesToLoad = simplifyBoxes(boxesToLoad);

        for (BBox bBox : simpleBoxesToLoad) {
            OSMData data = service.loadData(bBox.left, bBox.bottom, bBox.right, bBox.top);

            //todo сделать кэширование всех данных, а не только точек
            OSMData newData = new OSMData(data.nodes, Collections.<Long, Way>emptyMap(), Collections.<Long, Relation>emptyMap());
            cache.writeData(bBox, newData);
        }

        //читать из кэша
        return cache.getCachedData(box);
    }

    /**
     * Разбивает на атомарные элементы для загрузки
     *
     * @param boxes границы, которые нужно разбить на атомарные
     * @return атомарные элементы
     */
    private Collection<BBox> simplifyBoxes(Collection<BBox> boxes) {
        Set<BBox> result = new HashSet<BBox>(); //todo maybe use LinkedList ?

        for (BBox box : boxes) {
            result.addAll(box.simplify(NET_SIZE));
        }

        return result;
    }

}
